package tool

import (
	"bytes"
	"encoding/json"
	"github.com/hashicorp/go-uuid"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
)

var someIdClb = make(map[string]string, 0)

type ForwardList struct {
	Fl []KV `json:"forwardList"`
}
type KV struct {
	SomeId int    `json:"someId"`
	Clb    string `json:"clb"`
}

type Result struct {
	TraceId  string `json:"traceId"`
	HttpCode int    `json:"HttpCode"`
}

func Forward(w http.ResponseWriter, r *http.Request) {
	// 限制一些url访问
	if forbidden(r.URL.Path) {
		writeError(w, r, http.StatusForbidden)
		return
	}
	someId := r.Header.Get("headerId")
	if someId == "" {
		// 没有请求头，不允许转发
		writeError(w, r, http.StatusNotAcceptable)
		return
	}
	// 读取 String 类型配置
	if len(someIdClb) == 0 {
		someIdClb = getsomeIdClbMap()
	}
	clb, ok := someIdClb[someId]
	if !ok {
		clb = someIdClb[MinInt]
	}
	if clb == "" {
		writeError(w, r, http.StatusBadRequest)
		return
	}

	url := forwardURL(clb, r.URL.Path)
	body, err := io.ReadAll(r.Body)
	if http.MethodPost == r.Method && (len(body) == 0 || err != nil) {
		// 为post必须传递参数
		writeError(w, r, http.StatusBadRequest)
		return
	}
	req, err := http.NewRequest(r.Method, url, bytes.NewReader(body))
	if err != nil {
		writeError(w, r, http.StatusBadRequest)
		log.Printf("http.NewRequest err is %s\n", err)
		return
	}
	for name, vs := range r.Header {
		// 不区分大小写
		if strings.EqualFold("host", name) {
			continue
		}
		for _, v := range vs {
			// 设置要传递的Header信息
			req.Header.Set(name, v)
		}
	}

	// 发送Request请求，并处理回复信息
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("client.Do:%s", err)
		writeError(w, r, http.StatusBadGateway)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Printf("Body.Close:%s", err)
			return
		}
	}(resp.Body)

	for name, vs := range resp.Header {
		for _, v := range vs {
			w.Header().Set(name, v)
		}
	}
	w.WriteHeader(resp.StatusCode)
	trueResponse, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("io.ReadAll:", err)
		writeError(w, r, http.StatusInternalServerError)
		return
	}
	_, err = w.Write(trueResponse)
	if err != nil {
		log.Printf("w.Write:%s", err)
		return
	}
}

func writeError(w http.ResponseWriter, r *http.Request, code int) {
	// 错误码
	w.WriteHeader(code)
	// 请求信息在错误时打印日志，增加一个唯一id用于追踪排查
	traceId, _ := uuid.GenerateUUID()
	ret := Result{traceId, code}
	retStr, _ := json.Marshal(ret)
	log.Printf("traceId:%s,code:%d,Host:%s,URL:%v,Method:%s,Header:%v", traceId, code, r.Host, r.URL, r.Method, r.Header)
	_, err := w.Write(retStr)
	if err != nil {
		log.Printf("w.Write:%s", err)
	}
}

func forwardURL(clb string, path string) string {

	// fmt.Println("clb:", clb)
	if strings.Contains(clb, SLASH) {
		// 去掉clb中的所有 /
		clb = strings.Replace(clb, SLASH, "", -1)
	}
	// 确保url有前缀 /
	if !strings.HasPrefix(path, SLASH) {
		path = SLASH + path
	}
	// 去掉转发前缀
	if strings.HasPrefix(path, PREFIX) {
		path = strings.Replace(path, PREFIX, "", -1)
	}
	url := HTTP + clb + path
	log.Printf("forwardURL:%s", url)
	return url
}

func getsomeIdClbMap() map[string]string {
	err := getProxyConfig("")
	if err != nil {
		someIdClb[strconv.Itoa(-1)] = "127.0.0.1"
		return someIdClb
	}

	return someIdClb
}

func getProxyConfig(conf string) error {
	forwardList := ForwardList{}
	err := json.Unmarshal([]byte(conf), &forwardList)
	if err != nil {
		log.Printf("解析配置错误: %v", conf)
		return err
	}
	log.Printf("监听到变化，更新了配置 %v", forwardList)
	// 将七彩石配置的clb和someid放入map中
	for _, kv := range forwardList.Fl {
		someIdClb[strconv.Itoa(kv.SomeId)] = kv.Clb
	}
	return nil
}

func forbidden(path string) bool {
	for _, prefix := range forbiddenList {
		if strings.Contains(path, prefix) {
			return true
		}
	}
	return false
}

var forbiddenList = make([]string, 0)

const (
	HTTP   = "http://"
	MinInt = "-2147483648"
	SLASH  = "/"
	PREFIX = "/cgi/xxx"
)
